import dash
import time
import pandas as pd
from dash import html, dcc
import feffery_antd_components as fac
from dash.dependencies import Input, Output, State, ALL

from server import app
from models import DemoTable
from components import stores, modals, messages


app.layout = html.Div(
    [
        stores,
        modals,
        messages,

        # 用于初始化触发表格刷新
        dcc.Location(id='url'),

        # 表格渲染容器
        html.Div(
            id='table-operation-render'
        )
    ],
    style={
        'padding': '100px'
    }
)


@app.callback(
    Output('table-operation-render', 'children'),
    Input('url', 'pathname')
)
def initial_table_operation(pathname):

    return [
        fac.AntdText(
            '增删改一体化表格示例',
            style={
                'fontSize': '20px',
                'borderLeft': '3px solid #2e94f7',
                'paddingLeft': '5px',
            }
        ),
        fac.AntdSpin(
            fac.AntdTable(
                id='table-demo',
                columns=[
                    {
                        'title': f'字段{i}',
                        'dataIndex': f'field{i}',
                        'width': '33.33%'
                    }
                    for i in range(1, 4)
                ],
                data=(
                    pd
                    .DataFrame(
                        DemoTable.select().dicts()
                    )
                    # 将自增id字段重命名为key以便后续操作
                    .rename(
                        columns={
                            'id': 'key'
                        }
                    )
                    .sort_values('field3', ascending=False)
                    .to_dict('records')
                ),
                bordered=True,
                rowSelectionType='radio'
            )
        ),
        # 功能按钮
        fac.AntdSpace(
            [
                fac.AntdButton(
                    '新增记录',
                    id={
                        'type': 'table-operation-button',
                        'index': 'insert'
                    },
                    type='primary'
                ),
                fac.AntdButton(
                    '修改选中记录',
                    id={
                        'type': 'table-operation-button',
                        'index': 'alter'
                    },
                    type='primary'
                ),
                fac.AntdButton(
                    '删除选中记录',
                    id={
                        'type': 'table-operation-button',
                        'index': 'delete'
                    },
                    type='primary',
                    danger=True
                )
            ]
        )
    ]


# 处理增删改操作后的表格自动刷新
@app.callback(
    [Output('table-demo', 'data'),
     Output('table-demo', 'selectedRowKeys'),
     Output('table-demo', 'selectedRows')],
    Input(
        {
            'type': 'callback-store',
            'index': ALL
        },
        'data'
    ),
    prevent_initial_call=True
)
def update_table_after_operation(*args):

    if dash.ctx.triggered_id['index'] == 'delete':
        # 若本次由删除操作触发，则需将表格选中行相关状态清空
        return [
            (
                pd
                .DataFrame(
                    DemoTable.select().dicts()
                )
                .rename(
                    columns={
                        'id': 'key'
                    }
                )
                .sort_values('field3', ascending=False)
                .to_dict('records')
            ),
            [],
            []
        ]

    return [
        (
            pd
            .DataFrame(
                DemoTable.select().dicts()
            )
            .rename(
                columns={
                    'id': 'key'
                }
            )
            .sort_values('field3', ascending=False)
            .to_dict('records')
        ),
        dash.no_update,
        dash.no_update
    ]

# 处理增操作


@app.callback(
    Output(
        {
            'type': 'modal',
            'index': 'insert'
        },
        'visible'
    ),
    Input(
        {
            'type': 'table-operation-button',
            'index': 'insert'
        },
        'nClicks'
    )
)
def open_insert_modal(nClicks):
    '''
    打开新增记录模态框
    '''

    if nClicks:
        return True

    return False


@app.callback(
    [
        Output(
            {
                'type': 'callback-store',
                'index': 'insert'
            },
            'data'
        ),
        Output(
            {
                'type': 'message-container',
                'index': 'insert'
            },
            'children'
        )
    ],
    Input(
        {
            'type': 'modal',
            'index': 'insert'
        },
        'okCounts'
    ),
    [State('insert-field1', 'value'),
     State('insert-field2', 'value'),
     State('insert-field3', 'value')]
)
def insert_operation(okCounts, field1, field2, field3):
    '''
    通过新增记录模态框进行新纪录插入
    '''

    if all([okCounts, field1, field2, field3]):

        try:
            DemoTable.insert_one_record(
                field1=field1,
                field2=field2,
                field3=field3
            )

            # 触发对应store发生变化
            return [
                time.time(),
                fac.AntdMessage(
                    content='新增数据成功',
                    type='success'
                )
            ]

        except Exception as e:
            return [
                dash.no_update,
                fac.AntdMessage(
                    content='新增数据失败：'+str(e),
                    type='error'
                )
            ]

    elif okCounts:
        return [
            dash.no_update,
            fac.AntdMessage(
                content='请完善表单',
                type='warning'
            )
        ]

    return dash.no_update


# 处理改操作

@app.callback(
    [Output('alter-field1', 'value'),
     Output('alter-field2', 'value'),
     Output('alter-field3', 'value')],
    Input('table-demo', 'selectedRowKeys'),
    State('table-demo', 'selectedRows')
)
def prefill_alter_modal_form(selectedRowKeys, selectedRows):
    '''
    打开修改记录模态框前，预填充表单
    '''

    if selectedRowKeys:
        return [
            selectedRows[0]['field1'],
            selectedRows[0]['field2'],
            selectedRows[0]['field3']
        ]

    return [None] * 3


@app.callback(
    [
        Output(
            {
                'type': 'modal',
                'index': 'alter'
            },
            'visible'
        ),
        Output(
            {
                'type': 'message-container',
                'index': 'alter-select'
            },
            'children'
        )
    ],
    Input(
        {
            'type': 'table-operation-button',
            'index': 'alter'
        },
        'nClicks'
    ),
    State('table-demo', 'selectedRowKeys')
)
def open_alter_modal(nClicks, selectedRowKeys):
    '''
    打开修改记录模态框
    '''

    if nClicks and selectedRowKeys:
        return True, None

    elif nClicks:

        return [
            False,
            fac.AntdMessage(
                content='请先选择行',
                type='warning'
            )
        ]

    return dash.no_update


@app.callback(
    [
        Output(
            {
                'type': 'callback-store',
                'index': 'alter'
            },
            'data'
        ),
        Output(
            {
                'type': 'message-container',
                'index': 'alter'
            },
            'children'
        )
    ],
    Input(
        {
            'type': 'modal',
            'index': 'alter'
        },
        'okCounts'
    ),
    [State('alter-field1', 'value'),
     State('alter-field2', 'value'),
     State('alter-field3', 'value'),
     State('table-demo', 'selectedRows')],
    prevent_initial_call=True
)
def alter_operation(okCounts, field1, field2, field3, selectedRows):
    '''
    通过修改记录模态框进行旧记录修改
    '''

    if all([okCounts, field1, field2, field3]):

        try:
            DemoTable.alter_one_record(
                id=selectedRows[0]['key'],
                field1=field1,
                field2=field2,
                field3=field3
            )

            # 触发对应store发生变化
            return [
                time.time(),
                fac.AntdMessage(
                    content='修改数据成功',
                    type='success'
                )
            ]

        except Exception as e:
            return [
                dash.no_update,
                fac.AntdMessage(
                    content='修改数据失败：'+str(e),
                    type='error'
                )
            ]

    elif okCounts:
        return [
            dash.no_update,
            fac.AntdMessage(
                content='请完善表单',
                type='warning'
            )
        ]

    return dash.no_update


# 处理删操作

@app.callback(
    [
        Output(
            {
                'type': 'callback-store',
                'index': 'delete'
            },
            'data'
        ),
        Output(
            {
                'type': 'message-container',
                'index': 'delete'
            },
            'children'
        )
    ],
    Input(
        {
            'type': 'table-operation-button',
            'index': 'delete'
        },
        'nClicks'
    ),
    State('table-demo', 'selectedRows'),
    prevent_initial_call=True
)
def delete_operation(nClicks, selectedRows):
    '''
    通过删除按钮进行选中记录的删除
    '''

    if nClicks and selectedRows:

        try:
            DemoTable.delete_one_record(
                id=selectedRows[0]['key']
            )

            # 触发对应store发生变化
            return [
                time.time(),
                fac.AntdMessage(
                    content='删除数据成功',
                    type='success'
                )
            ]

        except Exception as e:
            return [
                dash.no_update,
                fac.AntdMessage(
                    content='删除数据失败：'+str(e),
                    type='error'
                )
            ]

    elif nClicks:
        return [
            dash.no_update,
            fac.AntdMessage(
                content='请先选择行',
                type='warning'
            )
        ]

    return dash.no_update


if __name__ == '__main__':
    app.run_server(debug=True)
